package com.pronhub.cloud.shop.web.controller;


import com.pronhub.cloud.shop.domain.OrderInfo;
import com.pronhub.cloud.shop.domain.SeckillOrder;
import com.pronhub.cloud.shop.domain.User;
import com.pronhub.cloud.shop.mq.MQConstants;
import com.pronhub.cloud.shop.mq.OrderMessage;
import com.pronhub.cloud.shop.msg.SeckillCodeMsg;
import com.pronhub.cloud.shop.redis.MyRedisTemplate;
import com.pronhub.cloud.shop.redis.SeckillKeyPrefix;
import com.pronhub.cloud.shop.resolver.RedisValue;
import com.pronhub.cloud.shop.service.IOrderInfoService;
import com.pronhub.cloud.shop.service.ISeckillGoodService;
import com.pronhub.cloud.shop.service.ISeckillOrderService;
import com.pronhub.cloud.shop.web.vo.SeckillGoodVo;
import com.pronhub.shop.cloud.Result;
import lombok.AllArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("/order")
@AllArgsConstructor
public class OrderInfoController {

    private ISeckillGoodService seckillGoodService;

    //商品订单的服务
    private ISeckillOrderService seckillOrderService;

    //秒杀订单的服务
    private IOrderInfoService orderInfoService;

    //秒杀商品数量的Redis
    private MyRedisTemplate myRedisTemplate;

    //使用MQ的服务
    private RabbitTemplate rabbitTemplate;

    //本地的线程安全问题 数据结构问题
    public static ConcurrentHashMap<Long,Boolean> isStockOverMap = new ConcurrentHashMap();

    @RequestMapping("/{path}/doSeckill")
    public Result<String> doSeckill(Long seckillId , @PathVariable(name = "path") String path, String uuid , @RedisValue User user){
        //进行秒杀服务的订单处理这个类 需要做下面的逻辑判断
        //1.用户是否登录了
        if (user==null){
            //就是没有Token在Cookic里面了
            return Result.error(SeckillCodeMsg.LOGIN_TIME_OUT);
        }
        //2.参数是否存在问题
        if (seckillId==null||seckillId<0){
            return Result.error(SeckillCodeMsg.OP_ERROR);
        }
        //从Redis中获取地址,然后进行判断,前面的NewPath和后面的OldePath有什么遗言,是否过时
        String oldPath = myRedisTemplate.get(SeckillKeyPrefix.SECKILL_PATH, uuid, String.class);
        if(StringUtils.isEmpty(oldPath) || !oldPath.equals(path)){
            return Result.error(SeckillCodeMsg.ALREADY_SELL_OUt);
        }

        Boolean isStockOverSign = isStockOverMap.get(seckillId);
        //卖完了相当于
        if (isStockOverSign!=null&&isStockOverSign){
            return Result.error(SeckillCodeMsg.ALREADY_SELL_OUt);
        }


        //3.是否有对于应的商品
        //SeckillGoodVo seckillGoodVo = seckillGoodService.findById(seckillId);
        SeckillGoodVo seckillGoodVo = seckillGoodService.findByIdFromCache(seckillId);
        if (seckillGoodVo==null){
            return Result.error(SeckillCodeMsg.OP_ERROR);
        }
        //4是否在秒杀的时间内 如果不在给 返回时间
        Date now = new Date();
        if (now.getTime()<seckillGoodVo.getStartDate().getTime() ||
                now.getTime()>seckillGoodVo.getEndDate().getTime()
                ){
            return Result.error(SeckillCodeMsg.OP_ERROR);
        }
        //5 根据UserId和订单数据,去查是否存在这个订单 如果存在就不给这个用户进行下单服务
        /*SeckillOrder seckillOrder = seckillOrderService.findByUserIdAndSeckillId(user.getId(),seckillId);
        if (seckillOrder!=null){
            return Result.error(SeckillCodeMsg.ALREADY_BY);
        }*/
        /**
         * 替换成从Redis里面操作  从Redis里面操作订单
         */
        boolean exists = myRedisTemplate.exists(SeckillKeyPrefix.SECKILL_ORDER, seckillId + ":" + user.getId());
        if (exists){
            //如果存在Redis中 就说明了早就买了
            return Result.error(SeckillCodeMsg.ALREADY_BY);
        }
        //6是否存在足够的库存消息
        if (seckillGoodVo.getStockCount()<=0){
            //当卖完了Redi 本地就设置成true
            isStockOverMap.put(seckillId,true);
            //没有足够的商品参与秒杀服务  卖完了抛出异常
            return Result.error(SeckillCodeMsg.ALREADY_SELL_OUt);
        }
        //用Redis的原子性来确定不超卖 由于Redis是单线程
        Long count = myRedisTemplate.decr(SeckillKeyPrefix.SECKILL_STOCK_COUNT, seckillId + "");
        if (count==0){
            //没有秒杀商品了 我们需要的是抛出信息给消费者没有商品了
            return Result.error(SeckillCodeMsg.ALREADY_SELL_OUt);
        }

        //7 下单服务 需要统一完成的 就是需要原子性 需要有事物的回滚机制
        // 顺序 7.1 从t_seckill_goods减去相对应的库存商品
        //      7.2 往订单表中间表t_seckill_order中存入哪个人在哪个场次的秒杀中秒杀了哪个商品的ID
        //      7.3 往t_order_info订单添加数据 谁在哪一场秒杀活动中买啥了商品
        // 返回的是一个订单号给前面
        /**
         * 2020年2月10日20:56:03
         * 改成了MQ的形式去完成下单任务,把需要的订单号发过去
         */
        //String orderNo = orderInfoService.insertOrder(user.getId(),seckillId);
        OrderMessage orderMessage = new OrderMessage(user.getId(), seckillId, uuid);
        rabbitTemplate.convertAndSend(MQConstants.ORDER_PEDDING_QUEUE,orderMessage);
        return Result.success("进入队列成功，请等待结果.",null);
    }

    //根据orderNo去查询相对于的订单
    @RequestMapping("/find")
    public Result<OrderInfo> find(String orderNo, @RedisValue User user){
        //1.传入到orderNo是否有效
        if(StringUtils.isEmpty(orderNo)){
            return Result.error(SeckillCodeMsg.OP_ERROR);
        }
        //2.登录了才能访问订单详情页面
        if(user==null){
            return Result.error(SeckillCodeMsg.LOGIN_TIME_OUT);
        }
        OrderInfo orderInfo = orderInfoService.find(orderNo);
        //3.只能访问自己的订单
        if(orderInfo==null || !orderInfo.getUserId().equals(user.getId())){
            //说明订单号码有问题，在数据库中无法查询到
            return Result.error(SeckillCodeMsg.OP_ERROR);
        }
        return Result.success(orderInfo);
    }
}
